package cn.bonoon.handler.impl;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.ThrowableCauseExtractor;
import org.springframework.web.filter.GenericFilterBean;

import cn.bonoon.kernel.util.RequestHelper;
import cn.bonoon.kernel.web.models.JsonResult;
/**
 * <pre>
 * 用于过滤器处理有异常返回的情况(在访问控制器之前的过滤出现异常的情况)：
 * 1.如果是普通的页面请求，则直接返回
 * 2.如果是ajax请求返回的异常数据
 * 
 * 如果在登录超时的情况下使用ajax访问，则应该弹出"登录超时"的提示
 * </pre>
 * @author jackson
 *
 */
public class SecurityExtendFilter extends GenericFilterBean{
	private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            chain.doFilter(request, response);
        } catch (IOException ex) {
            throw ex;
        } catch (Exception ex) {
        	if(RequestHelper.isAjax(request)){
	            // Try to extract a SpringSecurityException from the stacktrace
	            Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
	            Throwable ase = throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
	
	            if (ase == null) {
	                ase = throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
	            }
	            
	            if (ase != null) {
	        		response.setContentType("application/json;charset=UTF-8");
	        		ObjectMapper objectMapper = new ObjectMapper();
	        		JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), JsonEncoding.UTF8);

	    			try{
	    				JsonResult jsonData = JsonResult.logonError(ase);
	    				objectMapper.writeValue(jsonGenerator, jsonData);
	    			} catch (Exception e) {
	    				e.printStackTrace();
	    			}
	        		return;
	            }
        	}
            if (ex instanceof ServletException) {
                throw (ServletException) ex;
            }
            throw new RuntimeException(ex);
        }
	}

    /**
     * Default implementation of <code>ThrowableAnalyzer</code> which is capable of also unwrapping
     * <code>ServletException</code>s.
     */
    private static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer {
        /**
         * @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()
         */
        protected void initExtractorMap() {
            super.initExtractorMap();

            registerExtractor(ServletException.class, new ThrowableCauseExtractor() {
                public Throwable extractCause(Throwable throwable) {
                    ThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);
                    return ((ServletException) throwable).getRootCause();
                }
            });
        }

    }

}
